import * as XLSX from 'xlsx'
import * as  XLSX2 from 'xlsx-style';
import { MessageBox, Message } from 'element-ui'
// const XLSX2 = window.XLSX
/**
 * @description 表格导出
 * @param {*} dataSource 表格数据源
 * @param {*} datumKey 数据源合并行时得基准key
 * @param {*} header 表头设置数据（多层结构）
 * 如 header = [
 *           {prop: 'num', title: '序号',},
 *           {prop: 'name',
 *            title: '名字',
 *            merge: true,
 *            colorFun:(str)=>{
                  if(str == 0) return '00ff00'
                      return '0000ff'
              },
              numFmt:$numFmt.filter({1:"男",2:'女'})
`
            },
 *           {prop: '', title: '个人信息', valueType: 'd', width: 300,
 *               children: [
 *                    { prop: 'code', title: '年龄', merge: true, valueType: 'd', width: 300, },
 *                    { prop: 'height', title: '身高', valueType: 's', width: 300 },
 *                    { prop: '', title: '时间', valueType: 'n', width: 300,
 *                       children: [
 *                          { prop: 'year', title: '年份', valueType: 'd', width: 300, },
 *                          { prop: 'day', title: '日期', valueType: 's', width: 300 }
 *                         ]
 *                    }
 *                ]
 *            }
 *        ]
 * @returns
 */
function Json2Excel({ dataSource, datumKey, header, sheet = true, fileName = new Date().toDateString(), sheetSize = 5000 } = {}) {
  console.log("---------------处理表头合并数据-------------------");

  try {
    // 处理表头合并数据
    const headMerges = mergesHead(header)
    // 处理表格数据--真实得表格头
    const realCols = getRealCols(header)

    const headOption = {
      headMap: {},//表头key与title得对照Map
      headPropKeys: [],//所有得表头key得集合
      mergeCols: realCols
    }

    realCols.forEach(item => {
      headOption.headMap[item.prop] = item.title
      headOption.headPropKeys.push(item.prop)
    })
    console.log(headOption, 'headOption');

    // 处理获取表格复杂头部数据及合并参数设置
    const { sheet, merges } = resetSheetHeader({ headMerges, headPropKeys: headOption.headPropKeys })


    const wb = XLSX.utils.book_new();
    // 创建workSheet页
    let ws = {}, bodyMerges = []
    // 根据sheetSize拆分表格数据sheet页
    dataSource = dataSource.length > 0 ? dataSource : [null]
    dataSource.reduce((pre, cur, index) => {
      if (index % sheetSize === 0) {//分页
        bodyMerges = getMerges({ mergeCols: headOption.mergeCols, dataSource: dataSource.slice(index, index + sheetSize), headLen: headMerges.length, datumKey });
        ws = {
          '!cols': headOption.headPropKeys.map((item, colIndex) => {
            return { wch: headOption.mergeCols[colIndex].width || 15 }
          }),//列宽
          '!merges': [],//合并参数
          '!ref': ''//显示范围
        }
        //添加合并参数
        ws["!merges"] = bodyMerges.concat(...merges)
        ws = Object.assign(ws, sheet)
        let encode_last = XLSX.utils.encode_col(headOption.headPropKeys.length - 1)
        // 设置表格数据展示范围区间
        ws["!ref"] = `A1:${encode_last}${sheetSize + headMerges.length}`
        XLSX.utils.book_append_sheet(wb, ws, `第${Math.ceil(index / sheetSize) + 1}页`);
      }
      cur && (ws = createWorkSheet(ws, cur, index))
      return pre
    }, [])

    function createWorkSheet(ws, item, index) {
      headOption.headPropKeys.forEach((key, colIndex) => {
        // 获取表格数据体合并参数
        const cell = XLSX.utils.encode_cell({ c: colIndex + 0, r: index % sheetSize + headMerges.length })
        ws[cell] = {
          t: headOption.mergeCols[colIndex].valueType || 's',
          v: item[key] ? item[key] : headOption.mergeCols[colIndex].valueType === 'n' ? 0 : '/',
          s: {
            numFmt: headOption.mergeCols[colIndex].numFmt || '',
            border: {
              top: { style: 'thin', color: { rgb: "000000" } },
              left: { style: 'thin', color: { rgb: "000000" } },
              bottom: { style: 'thin', color: { rgb: "000000" } },
              right: { style: 'thin', color: { rgb: "000000" } },
            },
            font: {
              name: '宋体',//表示字体名称的字符串 字体名称，默认值是 "Calibri"
              sz: 12,//表示字号的数字 字号
              color: {// 字体颜色
                rgb: 'colorFun' in headOption.mergeCols[colIndex] ? headOption.mergeCols[colIndex].colorFun(item[key],item) : (item.bgColor || '000000')

              },
            },
            alignment: {
              vertical: 'center',//'"bottom"、"center" 或 "top" 垂直对齐
              horizontal: 'center',//'"left"、"center" 或 "right" 水平对齐
              wrapText: true,//' true 或 false 自动换行
            }
          }
        }
      })
      return ws
    }
    // XLSX.utils.book_append_sheet(wb, sheet1, "json1数据");
    const workbook = workbook2blob(wb)
    openDownloadDialog(workbook, `${fileName}.xlsx`);
    return true
  } catch (error) {
    console.error(error)
    Message({ message: `导出结果异常:` + error, type: 'error' })
    return false
  }
}


/**
 * 设置表格header数据
 * @param {*} headMerges
 * @returns {*} 期望得表格头部数据(复杂表格头)
 */
function resetSheetHeader({ headMerges, headPropKeys } = {}) {
  const merges = []
  let sheet = {}
  headMerges.forEach((row, rowIndex) => {
    row.forEach((col, colIndex) => {
      if (col.key === '') {//补全
        const obj = setMergeColSheet(col.curCol, col.curCol + col.colSpan - 1, col.curRow, col)
        sheet = Object.assign(sheet, obj)
        merges.push({ s: { c: col.curCol - 1, r: col.curRow - 1 }, e: { c: col.curCol + col.colSpan - 1 - 1, r: col.curRow - 1 } })
      } else {
        // 行合并
        const obj = setMergeRowSheet(col.curRow, headMerges.length, col.curCol, col)
        sheet = Object.assign(sheet, obj)
        if (rowIndex === headMerges.length - 1) return
        merges.push({ s: { c: col.curCol - 1, r: col.curRow - 1 }, e: { c: col.curCol - 1, r: headMerges.length - 1 } })

      }

    })

  })
  return {
    sheet,
    merges
  }
}
function setMergeRowSheet(startIndex, endIndex, curCol, obj) {
  const sheet = {}
  for (let i = startIndex; i <= endIndex; i++) {
    let encode_colKey = XLSX.utils.encode_col(curCol - 1)
    sheet[encode_colKey + i] = {
      v: obj.title,
      t: 's',
      s: {
        font: { name: "宋体", sz: 12, color: { auto: 1 }, bold: true },
        // 单元格对齐方式
        alignment: {
          /// 自动换行
          wrapText: 1,
          // 水平居中
          horizontal: "center",
          // 垂直居中
          vertical: "center"
        },
        border: {
          top: { style: 'thin', color: { rgb: "000000" } },
          left: { style: 'thin', color: { rgb: "000000" } },
          bottom: { style: 'thin', color: { rgb: "000000" } },
          right: { style: 'thin', color: { rgb: "000000" } },
        },
      }
    }
  }
  return sheet
}
function setMergeColSheet(startIndex, endIndex, curRow, obj) {
  const sheet = {}
  for (let i = startIndex; i <= endIndex; i++) {
    let encode_colKey = XLSX.utils.encode_col(i - 1)
    sheet[encode_colKey + curRow] = {
      v: obj.title,
      t: 's',
      s: {
        font: { name: "宋体", sz: 12, color: { auto: 1 }, bold: true },
        // 单元格对齐方式
        alignment: {
          /// 自动换行
          wrapText: 1,
          // 水平居中
          horizontal: "center",
          // 垂直居中
          vertical: "center"
        },
        border: {
          top: { style: 'thin', color: { rgb: "000000" } },
          left: { style: 'thin', color: { rgb: "000000" } },
          bottom: { style: 'thin', color: { rgb: "000000" } },
          right: { style: 'thin', color: { rgb: "000000" } },
        },
      }

    }
  }

  return sheet
}
function getRealCols(data) {
  const res = []
  data.forEach(item => {
    item.merge = item.merge ? true : false
    if (item.hasOwnProperty('children') && Array.isArray(item.children) && item.children.length > 0) {
      const child = getRealCols(item.children)
      res.push(...child)
    } else {
      res.push(item)
    }
  })
  return res
}

/**
 * mergeCols 列key的集合带有合并标识 merge
 * dataSource json数据源
 * headLen 表头得长度（占用得行数）
 */
function getMerges({ mergeCols, dataSource, headLen, datumKey } = {}) {
  let result = [];//各列合并数据集合
  mergeCols.forEach((col, colIndex) => {
    if (col.merge) {
      let mergeObj = null;
      const mergeArr = dataSource.reduce((prev, cur, index, data) => {
        // 与下一行比对，内容是否一致（前一列也相同方可合并）
        // console.log(cur,'cur');
        // console.log(col,'col',cur[col.prop]);
        // console.log(data[index + 1],'data[index + 1]',data[index + 1][col.prop]);
        // console.log(cur,'cur[datumKey] ',cur[datumKey],'===', data[index + 1][datumKey] );
        // console.log('----------------------------------');
        function setMergeObj(params) {
          if (mergeObj) {//更新合并单元格
            mergeObj.e = { c: colIndex, r: headLen + index + 1 };
          } else {//创建合并单元格
            mergeObj = {
              s: { c: colIndex, r: headLen + index },
              e: { c: colIndex, r: headLen + index + 1 }
            };
          }
        }
        function ClearMergeObj(params) {
          if (mergeObj) {
            const { e, s } = mergeObj;
            if (s.c != e.c || s.r != e.r) { //去除冗余数据
              prev.push(mergeObj);//注入合并数据
            }
          }
          mergeObj = null;//重置合并数据
        }
        if (//比对当前列
          index < data.length - 1 &&
          cur[col.prop] === data[index + 1][col.prop]
        ) {
          // 对比基准列是否一致，未设置基准列则对比第一列
          if (datumKey) {
            if (cur[datumKey] === data[index + 1][datumKey]) {
              setMergeObj()
            } else {
              ClearMergeObj()
            }
          } else {
            if (cur[mergeCols[0].prop] === data[index + 1][mergeCols[0].prop]) {
              setMergeObj()
            } else {
              ClearMergeObj()
            }
          }





        } else {
          ClearMergeObj()

        }
        return prev;
      }, []);
      result.push(...mergeArr);//解构各列合并数据
    }
  });

  return result;
}

/**
 *
 * @param {*} headData 无限级表头数据
 *   [
 *      {
 *        prop: '',
 *        title: '名字',
 *        children: [
 *           { prop: 'time', title: '时间',merge:true},
 *           { prop: 'name', title: '名称'},
 *           { prop: 'price', title: '价格'}
 *        ]
 *      },
 *      {
 *        prop: 'code',
 *        title: '编码',
 *        merge:true
 *      }
 *   ]
 * @returns
 */
function mergesHead(headData) {
  let newArr = []
  function each(data, index, parent) {
    if (newArr[index] === undefined) {
      newArr[index] = [];
    }
    let colTotal = 0
    data.forEach((e, ii) => {
      e.key = e.prop
      e.rowSpan = 1
      e.colSpan = 1
      e.curRow = index + 1

      if (e.children) {

        e.colSpan = gerMaxCol(e.children);
        e.rowSpan = getMaxFloor(e.children) + 1;
      }
      if (!parent) {
        if (ii === 0) {
          e.curCol = 1
        } else {
          e.curCol = data[ii - 1].curCol + data[ii - 1].colSpan
        }
      } else {
        e.curCol = parent.curCol + colTotal
      }
      colTotal += e.colSpan
      const { prop, children, merge, ...obj } = e
      newArr[index].push({
        ...obj
      });
      if (e.children && e.children.length > 0) {
        each(e.children, index + 1, e);
      }
    });
    return newArr
  }

  return each(headData, 0, '');
}
function gerMaxCol(items) {
  let max = 0;
  function each(data) {
    data.forEach((item) => {
      if (item.children) {
        each(item.children);
      } else {
        max = 1 + max;
      }
    });
  }
  each(items);
  return max;
}
const getMaxFloor = (treeData) => {
  let max = 0;
  function each(data, floor) {
    data.forEach((e) => {
      if (floor > max) {
        max = floor;
      }
      if (e.children && e.children.length > 0) {
        each(e.children, floor + 1);
      }
    });
  }
  each(treeData, 1);
  return max;
}


// 将一个sheet转成最终的excel文件的blob对象，然后利用URL.createObjectURL下载
function workbook2blob(workbook) {
  // 生成excel的配置项
  var wopts = {
    // 要生成的文件类型
    bookType: "xlsx",
    // // 是否生成Shared String Table，官方解释是，如果开启生成速度会下降，但在低版本IOS设备上有更好的兼容性
    bookSST: false,
    type: "binary"
  };
  var wbout = XLSX2.write(workbook, wopts);
  // 将字符串转ArrayBuffer
  function s2ab(s) {
    var buf = new ArrayBuffer(s.length);
    var view = new Uint8Array(buf);
    for (var i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xff;
    return buf;
  }
  var blob = new Blob([s2ab(wbout)], {
    type: "application/octet-stream"
  });
  return blob;
}
/**
 * 通用的打开下载对话框方法，没有测试过具体兼容性
 * @param url 下载地址，也可以是一个blob对象，必选
 * @param saveName 保存文件名，可选
 */
function openDownloadDialog(url, saveName) {
  if (typeof url == 'object' && url instanceof Blob) {
    url = URL.createObjectURL(url); // 创建blob地址
  }
  var aLink = document.createElement('a');
  aLink.href = url;
  aLink.download = saveName || ''; // HTML5新增的属性，指定保存文件名，可以不要后缀，注意，file:///模式下不会生效
  var event;
  if (window.MouseEvent) event = new MouseEvent('click');
  else {
    event = document.createEvent('MouseEvents');
    event.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
  }
  aLink.dispatchEvent(event);
}
export default Json2Excel




/**
 * demo
 * var dataSource = [
    {
        name: "孙",
        code: 1,
        num: 13,
        time: "2012-02-11 12:20:12",
        height: '290cm'
    },
    {
        name: "赵",
        code: 2,
        num: 23,
        time: "2012-02-11 12:20:12",
        tel: '0451',
        price: '$12.21',
        height: '90cm'
    },
    {
        name: "李",
        num: 66,
        code: 2,
        tel: '0451',
        time: "2012-02-11 12:20:12",
        height: '120cm'
    },
    {
        name: "李",
        code: 2,
        num: 662,
        time: "2012-02-11 12:20:12",
        tel: '0451',
        price: '$12.21',
        height: '190cm'
    }
];
var header = [
    {prop: 'num', title: '序号',},
    {prop: 'name',title: '名字', merge: true,},
    {prop: '', title: '个人信息', valueType: 'd', width: 300,
        children: [
            { prop: 'code', title: '年龄', merge: true, valueType: 'd', width: 300, },
            { prop: 'height', title: '身高', valueType: 's', width: 300 },
            {
                prop: '', title: '时间', valueType: 'n', width: 300,
                children: [
                    { prop: 'year', title: '年份', valueType: 'd', width: 300, },
                    { prop: 'day', title: '日期', valueType: 's', width: 300 }
                ]
            }
        ]
    }
];
 {
     fill: {
         patternType: '',// 图案样式
         bgColor: '',// 背景色，设置填充时的图案颜色
         fgColor: '',//前景色，单元格的背景颜色
     },
     border: {
         top: { style: 'thin', color: { rgb: "000000" } },
         left: { style: 'thin', color: { rgb: "000000" } },
         bottom: { style: 'thin', color: { rgb: "000000" } },
         right: { style: 'thin', color: { rgb: "000000" } },
     },
     font: {
         name: '',//表示字体名称的字符串 字体名称，默认值是 "Calibri"
         sz: 14,//表示字号的数字 字号
         color: {// 字体颜色
             rgb: 'e8b300'
         },
         bold: '',// true 或 false 加粗
         underline: '',// true 或 false 下划线
         italic: '',//true 或 false 倾斜
         strike: '',//true 或 false 删除线
         vertAlign: '',//'superscript' 或 'subscript' 上标 或 下标
     },
     numFmt: '',
     alignment: {
         vertical: '',//'"bottom"、"center" 或 "top" 垂直对齐
         horizontal: '',//'"left"、"center" 或 "right" 水平对齐
         wrapText: '',//' true 或 false 自动换行
         readingOrder: '',//' 0、1 或 2 文字方向
         textRotation: '',//' 数字，0至180 或 255 （默认为0） 文本旋转角度
     }
 }
 *
 */

